using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Sustainalytics.ESGRatings.BusinessLogic.Computing.Exceptions;
using Sustainalytics.Utils;

namespace Sustainalytics.ESGRatings.BusinessLogic.Computing
{
    internal class GovernanceSetList
    {
        private readonly object _lock = new object();
        private static int _currentId;
        private readonly ConcurrentDictionary<Guid, GovernanceSet> _governanceSetList;

        public GovernanceSetList()
        {
            _governanceSetList = new ConcurrentDictionary<Guid, GovernanceSet>();
        }

        public bool Delete(Guid id)
        {
            if (id.Equals(Guid.Empty))
            {
                throw new ArgumentOutOfRangeException("id");
            }

            GovernanceSet set;
            return _governanceSetList.TryRemove(id, out set);
        }

        public bool Delete(IEnumerable<Guid> setsIds)
        {
            if (setsIds == null)
            {
                throw new ArgumentNullException("setsIds");
            }

            lock (_lock)
            {
                GovernanceSet set;
                return setsIds.Aggregate(true, (current, id) => current && _governanceSetList.TryRemove(id, out set));
            }
        }

        public ICollection<GovernanceSet> GetSetByIdOrDefault(Guid? id)
        {
            Guid setId;

            if (id.Equals(Guid.Empty) || !id.HasValue)
            {
                var result = _governanceSetList.First();
                if (result.Equals(default(KeyValuePair<Guid, GovernanceSet>)))
                {
                    throw new UnknownSetException();
                }
                setId = result.Key;
            }
            else
            {
                setId = id.Value;
            }

            IList<GovernanceSet> sets;
            GetSetByGuid(setId, out sets);
            return sets;
        }

        public bool GetSetByGuid(Guid id, out IList<GovernanceSet> sets)
        {
            if (id == Guid.Empty)
            {
                throw new ComputeException("Invalid set id");
            }
            sets = new List<GovernanceSet>();

            GovernanceSet set;
            var result = _governanceSetList.TryGetValue(id, out set);
            if (set != null)
            {
                sets.Add(set);
            }

            return result;
        }

        public IEnumerable<Guid> GetSetId(int id)
        {
            var result = _governanceSetList.Where(x => x.Value.Id == id);
            return result.Select(x => x.Key).Distinct();
        }

        public int NextId()
        {
            if (_governanceSetList.IsNullOrEmpty())
            {
                _currentId = 0;
            }
            else
            {
                _currentId += 1;
            }

            return _currentId;
        }

        public bool TryAdd(Guid id, GovernanceSet set)
        {
            if (id.Equals(Guid.Empty))
            {
                throw new ComputeException("Invalid set id");
            }

            return _governanceSetList.TryAdd(id, set);
        }
    }
}